package fag.editor;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.EventListenerList;

import fag.core.collision.Gjk;
import fag.core.collision.Hitbox;
import fag.core.geometry.Circle2D;
import fag.core.geometry.Polygon2D;
import fag.core.geometry.Rectangle2D;
import fag.core.geometry.Vector2D;
import fag.core.model.Frame;

public class FrameEditor extends JPanel implements MouseListener,MouseMotionListener {

	private static final long serialVersionUID = -5414823915315525462L;
	
	private final static int DEFAULT_WIDTH = 400;
	private final static int DEFAULT_HEIGHT = 400;

	protected final static String ACTION_REPAINT_NEEDED = "Repaint Needed";
	protected final static String ACTION_RESET_SHAPE_SELECTION = "Reset Shape Selection";
	
	private final EventListenerList listeners = new EventListenerList();
	
	private double posCenterX;
	private double posCenterY;
	private AffineTransform transformation = new AffineTransform();
	
	private Frame frame = new Frame();
	
	// State
	private String currentMode = "none";
	private String currentShape = "none";
	private int zoom = 1;
	private double clicX = 0;
	private double clicY = 0;
	
	// Shape
	private boolean shapePressed = false;
	private boolean mouseDragged = false;
	private int shapeState;
	private String shapeType = "none";
	
	// Polygon
	private ArrayList<Vector2D> cloud = new ArrayList<Vector2D>();
	private List<Vector2D> polygonVertices = new ArrayList<Vector2D>();
	private double pointX,pointY =0;
	
	
	// Rectangle
	private double leftCornerX,leftCornerY,rectWidth,rectHeight = 0;
	
	// Circle
	private double centerX,centerY,radius = 0;
	
	// Sprite
	private Vector2D offset = new Vector2D();
	private int imageState = 0;
	private boolean imagePressed = false;
	
	
	
	
	public FrameEditor() {
		
		addMouseListener(this);
		addMouseMotionListener(this);
	}
	
	public void updateCenter() {
		
		posCenterX = getWidth() / 2;
		posCenterY = getHeight() / 2;
		repaintNeeded();
	}
	
	public void setZoom(int i) {
		
		zoom = i;
		transformation.scale(2,2);
	}
	
	public void setFrame(Frame f) {
		
		frame = f;
		updateMode();
		cloud.clear();
		repaintNeeded();
		
	}
	
	public void setBufferedImage(BufferedImage bi) {
		
		if(currentMode.equals("Sprite")) {
			
			frame.getSprite().resetBufferedImage();
			frame.setBufferedImage(bi);
			imageState = 1;
			repaintNeeded();
		}
	}
	
	public void setMode(String s) {
		
		if(!currentMode.equals(s)) {
			
			currentMode = s;
			updateMode();
		}
	}
	
	public void setShape(String s) {
		
		if(!currentShape.equals(s)) {
			
			currentShape = s;
			
			if(!currentShape.equals("Polygon") && cloud.size() > 0)
				cloud.clear();
			
			frame.setHitbox(null);
			updateMode();
		}
	}
	
	@Override
	public Dimension preferredSize() {
		
		return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
	}
	
	public void addActionListener(ActionListener listener) {
		
		listeners.add(ActionListener.class,listener);
	}
	
	public void removeActionListener(ActionListener listener) {
		
		listeners.remove(ActionListener.class, listener);
	}
	
	@Override
	public void paintComponent(Graphics g) {
		
		Graphics2D g2d = (Graphics2D) g;
		
		Color oldColor;
		Stroke oldStroke;
		
		if(frame != null) {
						
			if(frame.getBufferedImage() != null) {
				
				g2d.drawImage(frame.getBufferedImage(),
							(int)(posCenterX+(frame.getOffSet().x)),
							(int)(posCenterY+(frame.getOffSet().y)),
							frame.getBufferedImage().getWidth(),
							frame.getBufferedImage().getHeight(),
							null);
			}
			
			Hitbox shape = frame.getHitbox();
				
			if(shape instanceof Rectangle2D) {
									
				Rectangle2D r = (Rectangle2D) shape;
				
				oldColor = g2d.getColor();
				oldStroke = g2d.getStroke();
				g2d.setStroke(new BasicStroke(2));
				g2d.setColor(Color.RED);
				
				g2d.drawRect((int)(posCenterX+r.getX()),
						   (int)(posCenterY+r.getY()),
						   (int)r.getWidth(),
						   (int)r.getHeight());
				
				g2d.setColor(oldColor);
				g2d.setStroke(oldStroke);
			}	
			
			if(shape instanceof Circle2D) {
				
				Circle2D c = (Circle2D) shape;
				
				oldColor = g2d.getColor();
				oldStroke = g2d.getStroke();
				g2d.setStroke(new BasicStroke(2));
				g2d.setColor(Color.RED);

				
				g2d.drawOval((int)(posCenterX+c.getX()-c.getRadius()),
						   (int)(posCenterY+c.getY()-c.getRadius()),
						   (int)c.getRadius()*2,
						   (int)c.getRadius()*2);
				
				g2d.setColor(oldColor);
				g2d.setStroke(oldStroke);
			}
			
			if(shape instanceof Polygon2D)  {

				Polygon2D p = (Polygon2D) shape;
				
				oldColor = g2d.getColor();
				oldStroke = g2d.getStroke();
				g2d.setStroke(new BasicStroke(2));
				g2d.setColor(Color.RED);
				
				for(int i=0 ; i<p.getVertices().size(); i++) {

					if(i == (p.getVertices().size()-1)) {
						
						g2d.drawLine((int)(posCenterX+p.getVertices().get(i).x),
								   (int)(posCenterY+p.getVertices().get(i).y),
								   (int)(posCenterX+p.getVertices().get(0).x),
								   (int)(posCenterY+p.getVertices().get(0).y));
					}
					
					else {
					
						g2d.drawLine((int)(posCenterX+p.getVertices().get(i).x),
								  (int)(posCenterY+p.getVertices().get(i).y),
								  (int)(posCenterX+p.getVertices().get(i+1).x),
								  (int)(posCenterY+p.getVertices().get(i+1).y));
					}
					
				}
				g2d.setColor(oldColor);
				g2d.setStroke(oldStroke);
			}
			
			if(cloud.size() > 0) {
				
				for(Vector2D v : cloud) {
				
				oldColor = g2d.getColor();
				g2d.setColor(Color.BLUE);
				
				g2d.drawOval((int)(posCenterX+v.x-4),(int)(posCenterY+v.y-4),8,8);
				g2d.fillOval((int)(posCenterX+v.x-4),(int)(posCenterY+v.y-4),8,8);
				
				g2d.setColor(oldColor);
				}
			}
		}
		
		oldColor = g2d.getColor();
		oldStroke = g2d.getStroke();
		g2d.setStroke(new BasicStroke(2));
		g2d.setColor(Color.RED);
		
		g2d.drawLine((int)posCenterX,(int)posCenterY-20,(int)posCenterX,(int)posCenterY+20);
		g2d.drawLine((int)posCenterX-20,(int)posCenterY,(int)posCenterX+20,(int)posCenterY);
		
		g2d.setColor(oldColor);
		g2d.setStroke(oldStroke);
	}
	
	@Override
	public void mouseClicked(MouseEvent e) {

		if(e.getButton() == MouseEvent.BUTTON2) {
						
			if(currentMode.equals("Hitbox") && currentShape.equals("Polygon") && shapeState ==0) {
				
				if(cloud.size() > 2) {
					
					polygonVertices = (Polygon2D.getConvexHull(cloud)).getVertices();
					cloud.clear();
					frame.setHitbox(new Polygon2D(polygonVertices));
					shapeState = 1;
					shapeType = "Polygon";
					repaintNeeded();
				}
				
				else {
					
					JOptionPane.showMessageDialog(null, "Besoin de 3 points ou plus !", "Editor Popup", JOptionPane.ERROR_MESSAGE);
				}
			}
		}
	}

	@Override
	public void mousePressed(MouseEvent e) {
		
		if(e.getButton() == MouseEvent.BUTTON1) {
		
			if(currentMode.equals("Hitbox")) {
				
				if(shapeState == 0) {
				
					if(currentShape.equals("Rectangle")) {
					
						leftCornerX = e.getX() - posCenterX;
						leftCornerY = e.getY() - posCenterY;
						shapeType = "Rectangle";
					}
		
					if(currentShape.equals("Circle")) {
						
						centerX = e.getX() - posCenterX;
						centerY = e.getY() - posCenterY;
						shapeType = "Circle";
					}
					
					if(currentShape.equals("Polygon")) {
					
						pointX = e.getX() - posCenterX;
						pointY = e.getY() - posCenterY;
						shapeType = "cloud";
					}
				}
				
				if(shapeState == 1) {
							
					if(shapeState == 1 && shapeType.equals("Rectangle")) {
						
						clicX = e.getX() - posCenterX;
						clicY = e.getY() - posCenterY;
						
						if(Gjk.getResult(new Vector2D(clicX,clicY),(Rectangle2D)frame.getHitbox()).isColliding()) {
							
							shapePressed = true;
						}
					}
					
					if(shapeState == 1 && shapeType.equals("Circle")) {
						
						clicX = e.getX() - posCenterX;
						clicY = e.getY() - posCenterY;
						
						if(Gjk.getResult(new Vector2D(clicX,clicY),(Circle2D)frame.getHitbox()).isColliding()) {
							
							shapePressed = true;
						}
					}
					
					if(shapeState == 1 && shapeType.equals("Polygon")) {
						
						clicX = e.getX() - posCenterX;
						clicY = e.getY() - posCenterY;
						
						if(Gjk.getResult(new Vector2D(clicX,clicY),(Polygon2D)frame.getHitbox()).isColliding()) {
							
							shapePressed = true;
						}
					}
				}
			}
			
			if(currentMode.equals("Sprite") && imageState == 1) {
				
				clicX = e.getX() - posCenterX;
				clicY = e.getY() - posCenterY;
				
				if(Gjk.getResult(new Circle2D(clicX,clicY,1),frame.getSprite().getBounds()).isColliding()) {
					
					imagePressed = true;
				}
			}
		}
	}
	
	@Override
	public void mouseDragged(MouseEvent e) {
		
		if(currentMode.equals("Hitbox") && shapeState == 0) {
			
			if (shapeType.equals("Rectangle")) {
			
				rectWidth = e.getX() - posCenterX - leftCornerX;
				rectHeight = e.getY() - posCenterY - leftCornerY;					
				frame.setHitbox(new Rectangle2D(leftCornerX,leftCornerY,rectWidth,rectHeight));
				mouseDragged = true;
				repaintNeeded();
			}
		
			if(shapeType.equals("Circle")) {
				
				double distX = e.getX() - posCenterX - centerX;
				double distY = e.getY() - posCenterY - centerY;
				radius = Math.sqrt(distX*distX + distY*distY);		
				frame.setHitbox(new Circle2D(centerX,centerY,radius));
				mouseDragged = true;
				repaintNeeded();
			}
		}
		
		if(currentMode.equals("Hitbox") && shapeState == 1  && shapePressed == true) {
			
			if(shapeType.equals("Rectangle")) {
			
				double secondClicX = e.getX() - posCenterX;
				double secondClicY = e.getY() - posCenterY;
				double moveX = secondClicX - clicX;
				double moveY = secondClicY - clicY;
				clicX = secondClicX;
				clicY = secondClicY;
				leftCornerX = leftCornerX + moveX;
				leftCornerY = leftCornerY + moveY;
				frame.setHitbox(new Rectangle2D(leftCornerX,leftCornerY,rectWidth,rectHeight));
				repaintNeeded();
			}
			
			if(shapeType.equals("Circle")) {
				
				double secondClicX = e.getX() - posCenterX;
				double secondClicY = e.getY() - posCenterY;
				double moveX = secondClicX - clicX;
				double moveY = secondClicY - clicY;
				clicX = secondClicX;
				clicY = secondClicY;
				centerX = centerX + moveX;
				centerY = centerY + moveY;
				frame.setHitbox(new Circle2D(centerX,centerY,radius));
				repaintNeeded();
			}
			
			if(shapeType.equals("Polygon")) {
				
				double secondClicX = e.getX() - posCenterX;
				double secondClicY = e.getY() - posCenterY;
				double moveX = secondClicX - clicX;
				double moveY = secondClicY - clicY;
				clicX = secondClicX;
				clicY = secondClicY;
				
				for(Vector2D v : polygonVertices) {

					v.x = v.x + moveX;
					v.y = v.y + moveY;
				}
				frame.setHitbox(new Polygon2D(polygonVertices));
				repaintNeeded();
			}
		}
		
		if(currentMode.equals("Sprite") && imageState == 1 && imagePressed == true) {
			
			double secondClicX = e.getX() - posCenterX;
			double secondClicY = e.getY() - posCenterY;
			double moveX = secondClicX - clicX;
			double moveY = secondClicY - clicY;
			clicX = secondClicX;
			clicY = secondClicY;
			offset.x = offset.x + moveX;
			offset.y = offset.y + moveY;
			frame.setOffSet(new Vector2D(offset.x,offset.y));
			repaintNeeded();
		}
	}

	@Override
	public void mouseReleased(MouseEvent e) {

		if(e.getButton() == MouseEvent.BUTTON1) {
			
			if(currentMode.equals("Hitbox")) {
			
				if(currentShape.equals("Rectangle")) {
	
					if(shapeState == 0 && mouseDragged == true) {
						
						shapeState = 1;
						mouseDragged = false;
						return;
					}
					
					if(shapeState == 1) {
						
						shapePressed = false;
					}
				}
					
				if(currentShape.equals("Circle")) {

					if(shapeState == 0 && mouseDragged == true) {
						
						shapeState = 1;
						mouseDragged = false;
						return;
					
					}
					
					if(shapeState == 1) {
						
						shapePressed = false;
					}
				}
					
				if(currentShape.equals("Polygon")) {
					
					if(shapeState == 0) {
						
						Vector2D point = new Vector2D(pointX,pointY);
						cloud.add(point);
						repaintNeeded();
					}
					
					if(shapeState == 1) {
						
						shapePressed = false;
					}
				}
			}		
			
			if(currentMode.equals("Sprite") && imageState == 1) {
				
				imagePressed = false;
			}
		}
		
		if(e.getButton() == MouseEvent.BUTTON3) {
			
			if(currentMode.equals("Hitbox")) {
			
				if(shapeType.equals("Rectangle")) {
				
					frame.setHitbox(null);
					leftCornerX = 0;
					leftCornerY = 0;
					rectWidth = 0;
					rectHeight = 0;
					shapeState = 0;
					clicX = 0;
					clicY = 0;
					repaintNeeded();
				}
				
				if(shapeType.equals("Circle")) {
					
					frame.setHitbox(null);
					centerX = 0;
					centerY = 0;
					radius = 0;
					shapeState = 0;
					clicX = 0;
					clicY = 0;
					repaintNeeded();
				}
				
				if(shapeType.equals("Polygon")) {
					
					frame.setHitbox(null);
					cloud.clear();
					polygonVertices.clear();
					pointX = 0;
					pointY = 0;
					clicX = 0;
					clicX = 0;
					shapeState = 0;
					repaintNeeded();
				}
			}
			
			if(currentMode.equals("Sprite")) {
				
				frame.setBufferedImage(null);
				offset = new Vector2D();
				imageState = 0;
				clicX = 0;
				clicY = 0;
				repaintNeeded();
			}
		}
	}
	
	@Override
	public void mouseEntered(MouseEvent e) {}

	@Override
	public void mouseExited(MouseEvent e) {}
	
	@Override
	public void mouseMoved(MouseEvent e) {}
	
	
	protected void repaintNeeded() {
		
		for(ActionListener listener : listeners.getListeners(ActionListener.class)) 
			listener.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_LAST + 1,ACTION_REPAINT_NEEDED));
	}
	
	protected void resetShapeSelection() {
		
		for(ActionListener listener : listeners.getListeners(ActionListener.class))
			listener.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_LAST +1,ACTION_RESET_SHAPE_SELECTION));
	}
	
	private void updateMode() {
		
		if(currentMode.equals("Sprite")) {
			
			if(frame.getBufferedImage() != null)
				imageState = 1;
			else
				imageState = 0;
		}
		
		if(currentMode.equals("Hitbox")) {
			
			if(frame.getHitbox() != null) {
					
				shapeState = 1;
				
				if(frame.getHitbox() instanceof Rectangle2D)
					shapeType = "Rectangle";
				
				if(frame.getHitbox() instanceof Circle2D)
					shapeType = "Circle";
				
				if(frame.getHitbox() instanceof Polygon2D)
					shapeType = "Polygon";
			}
			
			else {
				
				shapeState = 0;
				shapeType = "none";
			}
		}
	}
}
